<template>
  <a-menu
    v-model:open-keys="openedKeys"
    v-model:selectedKeys="selectKeys"
    v-bind="$attrs"
    class="antd-menu"
    @select="onSelect"
    @deselect="onDeselect"
  >
    <sub-menu
      v-for="item of menus"
      :menu="item"
      :fields="fields"
      :popupClassName="['antd-menu-popup-menu', popupClassName].join(' ')"
      @titleClick="(e) => $emit('titleClick', e)"
    />
  </a-menu>
</template>

<script>
import { computed, watch, ref, h } from 'vue';
import { MenuOutlined } from '@ant-design/icons-vue';
import Menu from 'ant-design-vue/es/menu/index.js';

const AIcon = {
  name: 'AIcon',
  displayName: 'AIcon',
  props: {
    src: [Object, String, Function],
  },
  setup(props, { attrs }) {
    const src = props.src || MenuOutlined;
    const style = { width: '1em', height: '1em' };
    return () => {
      const isStr = typeof src === 'string' && src;
      return isStr ? h('img', { style, src, ...attrs }, null) : src ? h(src, { style, ...attrs }) : null;
    };
  },
};

const SubMenu = {
  name: 'SubMenu',
  emits: ['titleClick'],
  props: {
    menu: Object,
    fields: Object,
    popupClassName: String,
  },
  setup(props, { emit }) {
    const { menu, fields, popupClassName } = props;

    function onTitleClick(scope) {
      emit('titleClick', scope);
    }

    function pickMenuAttrs(menu) {
      return Object.keys(menu).reduce((res, key) => {
        if (typeof menu[key] === 'string') {
          res[key] = menu[key];
        }
        return res;
      }, {});
    }

    return () => {
      if (menu && menu[fields.children] && menu[fields.children].length) {
        const opt = { key: menu[fields.key], popupClassName, class: 'antd-menu-submenu' };
        return h(Menu.SubMenu, opt, {
          icon: () => h(AIcon, { src: menu[fields.icon], title: menu[fields.title], class: 'antd-menu-icon' }),
          title: () => h('span', { ...pickMenuAttrs(menu) }, menu[fields.title]),
          default: () => {
            return menu[fields.children].map((item) => {
              const key = item[fields.key];
              return h(SubMenu, { key, menu: item, fields, popupClassName, onTitleClick });
            });
          },
        });
      } else if (menu) {
        const opt = { key: menu[fields.key], class: 'antd-menu-item' };
        return h(Menu.Item, opt, {
          icon: () => h(AIcon, { src: menu[fields.icon], title: menu[fields.title], class: 'antd-menu-icon' }),
          default: () => h('span', { ...pickMenuAttrs(menu), onClick: () => onTitleClick(menu) }, menu[fields.title]),
        });
      }
    };
  },
};

export default {
  name: 'antd-menu',
  components: { SubMenu },
  emits: ['update:selectedKeys', 'update:openKeys', 'change', 'select', 'deselect', 'titleClick'],
  props: {
    selectedKeys: { type: Array, default: () => [] },
    openKeys: { type: Array, default: () => [] },
    menus: { type: Array, default: () => [] },
    replaceFields: { type: Object, default: undefined },
    popupClassName: { type: String, default: undefined },
  },
  setup(props, context) {
    const fields = computed(() => {
      const opt = { key: 'key', title: 'title', icon: 'icon', children: 'children' };
      return Object.assign({}, opt, props.replaceFields);
    });
    const openedKeys = ref([]);
    const selectKeys = ref([]);

    function handleSelect({ selectedKeys }) {
      selectKeys.value = selectedKeys;
      context.emit('update:selectedKeys', selectedKeys);
    }
    function onSelect({ item, key, selectedKeys }) {
      handleSelect({ item, key, selectedKeys });
      setTimeout(() => {
        context.emit('select', { item, key, selectedKeys });
        context.emit('change', { item, key, selectedKeys });
      }, 0);
    }
    function onDeselect({ item, key, selectedKeys }) {
      handleSelect({ item, key, selectedKeys });
      setTimeout(() => {
        context.emit('deselect', { item, key, selectedKeys });
        context.emit('change', { item, key, selectedKeys });
      }, 0);
    }
    watch(
      () => props.selectedKeys,
      () => {
        selectKeys.value = props.selectedKeys;
      },
      { immediate: true }
    );
    watch(
      () => props.openKeys,
      () => {
        openedKeys.value = props.openKeys;
      },
      { immediate: true }
    );
    return { fields, openedKeys, selectKeys, onSelect, onDeselect };
  },
};
</script>

<style lang="less">
.antd-menu,
.antd-menu-popup-menu {
  .antd-menu-item,
  .antd-menu-submenu .ant-menu-submenu-title {
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    justify-content: flex-start;
    align-items: center;
  }
}
</style>
